//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File:        cryptui.cpp
//
// Contents:    Cert Server wrapper routines
//
//---------------------------------------------------------------------------

#include <pch.cpp>

#pragma hdrstop

#include "certmsg.h"
#include "clibres.h"
#include "setupids.h"
#include "tfc.h"
#include "Windowsx.h"

#define __dwFILE__	__dwFILE_INITLIB_CERTUI_CPP__


HRESULT
myGetConfigStringFromPicker(
    OPTIONAL IN HWND         hwndParent,
    OPTIONAL IN WCHAR const *pwszPrompt,
    OPTIONAL IN WCHAR const *pwszTitle,
    OPTIONAL IN WCHAR const *pwszSharedFolder,
    IN  BOOL                 fUseDS,
    OUT WCHAR              **ppwszConfig)
{
    HRESULT hr;
    DWORD   dwCACount;
    CRYPTUI_CA_CONTEXT const *pCAContext = NULL;

    hr = myGetConfigFromPicker(
			hwndParent,
			pwszPrompt,
			pwszTitle,
			pwszSharedFolder,
			fUseDS,
			FALSE,
			&dwCACount,
			&pCAContext);
    _JumpIfError(hr, error, "myGetConfigFromPicker");

    if (NULL == pCAContext)
    {
        hr = E_INVALIDARG;
        _JumpIfError(hr, error, "Internal error: myGetConfigFromPicker");
    }

    hr = myFormConfigString(
			pCAContext->pwszCAMachineName,
			pCAContext->pwszCAName,
			ppwszConfig);
    _JumpIfError(hr, error, "myFormConfigString");

error:
    if (NULL != pCAContext)
    {
        CryptUIDlgFreeCAContext(pCAContext);
    }
    return(hr);
}


HRESULT
myUIGetWindowText(
    IN HWND     hwndCtrl,
    OUT WCHAR **ppwszText)
{
    HRESULT  hr;
    LRESULT  len;
    DWORD    i;
    WCHAR   *pwszBegin;
    WCHAR   *pwszEnd;
    WCHAR   *pwszText = NULL;

    CSASSERT(NULL != hwndCtrl &&
             NULL != ppwszText);

    // init
    *ppwszText = NULL;

    // get text string size
    len = SendMessage(hwndCtrl, WM_GETTEXTLENGTH, 0, 0);
    if (0 < len)
    {
        pwszText = (WCHAR*)LocalAlloc(LMEM_FIXED, (UINT)((len+1) * sizeof(WCHAR)));
	if (NULL == pwszText)
	{
	    hr = E_OUTOFMEMORY;
	    _JumpError(hr, error, "LocalAlloc");
	}
        if (len !=
            SendMessage(hwndCtrl, WM_GETTEXT, (WPARAM)len+1, (LPARAM)pwszText))
        {
            hr = HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
            _JumpError(hr, error, "Internal error");
        }
    }
    else
    {
        goto done;
    }

    // trim trailing and heading blank strings
    pwszBegin = pwszText;
    pwszEnd = &pwszText[wcslen(pwszText) - 1];

    while (pwszEnd > pwszBegin && iswspace(*pwszEnd) )
    {
        *pwszEnd = L'\0';
         --pwszEnd;
    }
    while (pwszBegin <= pwszEnd &&
           L'\0' != *pwszBegin &&
           iswspace(*pwszBegin) )
    {
        ++pwszBegin;
    }

    if (pwszEnd >= pwszBegin)
    {
        MoveMemory(
	    pwszText,
	    pwszBegin,
	    (SAFE_SUBTRACT_POINTERS(pwszEnd, pwszBegin) + 2) * sizeof(WCHAR));
    }
    else
    {
        goto done;
    }

    *ppwszText = pwszText;
    pwszText = NULL;

done:
    hr = S_OK;
error:
    if (NULL != pwszText)
    {
        LocalFree(pwszText);
    }
    return hr;
}


// following code for CA selection UI control

HRESULT
UICASelectionUpdateCAList(
    HWND  hwndList,
    WCHAR const *pwszzCAList)
{
    HRESULT  hr;
    int      nItem;
    WCHAR const *pwszCA = pwszzCAList;

    // remove current list
    SendMessage(hwndList, CB_RESETCONTENT, (WPARAM) 0, (LPARAM) 0);

    // add to list
    while (NULL != pwszCA && L'\0' != pwszCA[0])
    {
        nItem = (INT)SendMessage(
                    hwndList,
                    CB_ADDSTRING,
                    (WPARAM) 0,
                    (LPARAM) pwszCA);
        if (LB_ERR == nItem)
        {
            hr = myHLastError();
            _JumpError(hr, error, "SendMessage");
        }
        pwszCA += wcslen(pwszCA) + 1;
    }

    if (NULL != pwszzCAList)
    {
        // attempt to choose the 1st one as default
        SendMessage(hwndList, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
    }
    hr = S_OK;

error:
    return hr;
}


LRESULT CALLBACK
myUICASelectionComputerEditFilterHook(
    HWND hwndComputer,
    UINT iMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    LRESULT  lr;
    HRESULT  hr;
    CERTSRVUICASELECTION *pData = (CERTSRVUICASELECTION*)
                 GetWindowLongPtr(hwndComputer, GWLP_USERDATA);
    CSASSERT(NULL != pData);

    switch(iMsg)
    {
        case WM_CHAR:
            // empty ca list
            hr = UICASelectionUpdateCAList(pData->hwndCAList, NULL);
            _PrintIfError(hr, "UICASelectionUpdateCAList");
        break;
    }

    lr = CallWindowProc(
		    pData->pfnUICASelectionComputerWndProcs,
		    hwndComputer,
		    iMsg,
		    wParam,
		    lParam);

//error:
    return lr;
}

HRESULT
myUICAConditionallyDisplayEnterpriseWarning(
    IN CERTSRVUICASELECTION *pData)
{
    HRESULT hr = S_OK;
    CAINFO  *pCAInfo = NULL;
    BOOL fCoInit = FALSE;
    
    hr = CoInitialize(NULL);
    if (S_OK != hr && S_FALSE != hr)
    {
        _JumpError(hr, Ret, "CoInitialize");
    }
    fCoInit = TRUE;
    hr = S_OK; // don't want to return this error 
    
pData->CAType = ENUM_UNKNOWN_CA;
    
    // pinging specific CA is done in both cases -- reselect or new machine pointed at
    WCHAR szCA[MAX_PATH];
    WCHAR szComputer[MAX_PATH];
    szCA[0]=L'\0';
    szComputer[0]=L'\0';
    int iSel;
    iSel = ComboBox_GetCurSel(pData->hwndCAList);
    ComboBox_GetLBText(pData->hwndCAList, iSel, szCA);
    GetWindowText(pData->hwndComputerEdit, szComputer, MAX_PATH);
    
    if ((szCA[0]==L'\0') || (szComputer[0]==L'\0'))
    {
        ShowWindow(GetDlgItem(pData->hDlg, IDC_CLIENT_WARN_ENTERPRISE_REQUIREMENTS), SW_HIDE);
        goto Ret;
    }
    
    hr = myPingCertSrv(
        szCA,
        szComputer,
        NULL,
        NULL,
        &pCAInfo,
        NULL,
        NULL);
    
    if ((hr == S_OK) && (pCAInfo != NULL))
{
// copy catype into returned data
pData->CAType = pCAInfo->CAType;

 if (IsEnterpriseCA(pCAInfo->CAType))
    {
        ShowWindow(GetDlgItem(pData->hDlg, IDC_CLIENT_WARN_ENTERPRISE_REQUIREMENTS), SW_SHOW);
        
    }
    else
    {
        ShowWindow(GetDlgItem(pData->hDlg, IDC_CLIENT_WARN_ENTERPRISE_REQUIREMENTS), SW_HIDE);
    }
}

    
Ret:
    if (NULL != pCAInfo)
        LocalFree(pCAInfo);
    
    if (fCoInit)
        CoUninitialize();
    
    return hr;
}


HRESULT
myUICAHandleCAListDropdown(
    IN int                       iNotification,
    IN OUT CERTSRVUICASELECTION *pData,
    IN OUT BOOL                 *pfComputerChange)
{
    HRESULT  hr;
    WCHAR   *pwszComputer = NULL;
    WCHAR   *pwszzCAList = NULL;
    BOOL     fCoInit = FALSE;
    WCHAR   *pwszDnsName = NULL;
    DWORD   dwVersion;

    CSASSERT(NULL != pData);



    // if this isn't a focus or selection change and computer name stayed same, nothing to do
    if ((CBN_SELCHANGE != iNotification) && !*pfComputerChange) 
    {
        goto done;
    }

    ShowWindow(GetDlgItem(pData->hDlg, IDC_CLIENT_WARN_ENTERPRISE_REQUIREMENTS), SW_HIDE);  
    SetCursor(LoadCursor(NULL, IDC_WAIT));

    if (NULL == pData->hwndComputerEdit)
    {
        // not init
        goto done;
    }

    // make sure computer edit field is not empty
    hr = myUIGetWindowText(pData->hwndComputerEdit,
                           &pwszComputer);
    _JumpIfError(hr, error, "myUIGetWindowText");
    if (NULL == pwszComputer)
    {
        goto done;
    }


if (*pfComputerChange)
{

    // ping to get ca list
    hr = CoInitialize(NULL);
    if (S_OK != hr && S_FALSE != hr)
    {
        _JumpError(hr, error, "CoInitialize");
    }
    fCoInit = TRUE;


    // reset once ca list is updated.  Do this now to prevent recursion
    *pfComputerChange = FALSE;

    hr = myPingCertSrv(
		pwszComputer,
		NULL,
		&pwszzCAList,
		NULL,
		NULL,
		&dwVersion,
                &pwszDnsName);
    CSILOG(hr, IDS_ILOG_GETCANAME, pwszComputer, NULL, NULL);
    if (S_OK != hr)
    {
        // make sure null
        CSASSERT(NULL == pwszzCAList);

	// can't ping the ca.  Set focus now to prevent recursion
	SetFocus(pData->hwndComputerEdit);
	SendMessage(pData->hwndComputerEdit, EM_SETSEL, 0, -1);

	CertWarningMessageBox(
		pData->hInstance,
		FALSE,
		pData->hDlg,
		IDS_WRN_PINGCA_FAIL,
		hr,
		NULL);
    }
    else if(dwVersion<2 && pData->fWebProxySetup)
    {
        //bug 262316: don't allow installing Whistler proxy to an older CA

        hr = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);

	if(pwszzCAList)
        {
            LocalFree(pwszzCAList);
            pwszzCAList = NULL;
        }

	SetFocus(pData->hwndComputerEdit);
	SendMessage(pData->hwndComputerEdit, EM_SETSEL, 0, -1);

	CertWarningMessageBox(
		pData->hInstance,
		FALSE,
		pData->hDlg,
		IDS_WRN_OLD_CA,
		hr,
		NULL);
    }

    if (NULL!=pwszDnsName && 0!=wcscmp(pwszComputer, pwszDnsName)) {
	// update computer
	SendMessage(pData->hwndComputerEdit, WM_SETTEXT,
		    0, (LPARAM)pwszDnsName);
    }

    // update ca list
    hr = UICASelectionUpdateCAList(pData->hwndCAList, pwszzCAList);
    _JumpIfError(hr, error, "UICASelectionUpdateCAList");
}

// pinging specific CA is done in both cases -- reselect or new machine pointed at
    hr = myUICAConditionallyDisplayEnterpriseWarning(pData);
    _PrintIfError(hr, "myUICAConditionallyDisplayEnterpriseWarning");

done:
    hr = S_OK;

error:
    SetCursor(LoadCursor(NULL, IDC_ARROW));

    if (fCoInit)
    {
        CoUninitialize();
    }
    if (NULL != pwszzCAList)
    {
        LocalFree(pwszzCAList);
    }
    if (NULL != pwszComputer)
    {
        LocalFree(pwszComputer);
    }
    if (NULL != pwszDnsName)
    {
        LocalFree(pwszDnsName);
    }
    return hr;
}


HRESULT
myInitUICASelectionControls(
    IN OUT CERTSRVUICASELECTION *pUICASelection,
    IN HINSTANCE                 hInstance,
    IN HWND                      hDlg,
    IN HWND                      hwndBrowseButton,
    IN HWND                      hwndComputerEdit,
    IN HWND                      hwndCAList,
    IN BOOL                      fDSCA,
    OUT BOOL			*pfCAsExist)
{
    HRESULT  hr;
    PCCRYPTUI_CA_CONTEXT  pCAContext = NULL;
    DWORD          dwCACount;
    CString cstrText;

    SetCursor(LoadCursor(NULL, IDC_WAIT));
    hr = myGetConfigFromPicker(
			  hDlg,
			  NULL,
			  NULL,
			  NULL,
                          fDSCA,
			  TRUE,	// fCountOnly
			  &dwCACount,
			  &pCAContext);
    SetCursor(LoadCursor(NULL, IDC_ARROW));
    if (S_OK != hr)
    {
        dwCACount = 0;
        _PrintError(hr, "myGetConfigFromPicker");
    }

    // enable/disable
    *pfCAsExist = 0 < dwCACount;
    EnableWindow(hwndBrowseButton, *pfCAsExist);

    // set computer edit control hook
    pUICASelection->pfnUICASelectionComputerWndProcs =
        (WNDPROC)SetWindowLongPtr(hwndComputerEdit,
             GWLP_WNDPROC, (LPARAM)myUICASelectionComputerEditFilterHook);

    pUICASelection->hInstance = hInstance;
    pUICASelection->hDlg = hDlg;
    pUICASelection->hwndComputerEdit = hwndComputerEdit;
    pUICASelection->hwndCAList = hwndCAList;

    // pass data to both controls
    SetWindowLongPtr(hwndComputerEdit, GWLP_USERDATA, (ULONG_PTR)pUICASelection);
    SetWindowLongPtr(hwndCAList, GWLP_USERDATA, (ULONG_PTR)pUICASelection);

    // by default, don't show Enterprise CA warning
    cstrText.LoadString(IDS_WARN_ENTERPRISE_REQUIREMENTS);
    SetWindowText(GetDlgItem(hDlg, IDC_CLIENT_WARN_ENTERPRISE_REQUIREMENTS), cstrText);
    ShowWindow(GetDlgItem(hDlg, IDC_CLIENT_WARN_ENTERPRISE_REQUIREMENTS), SW_HIDE);

    if (NULL != pCAContext)
    {
        CryptUIDlgFreeCAContext(pCAContext);
    }

    hr = S_OK;
//error:
    return hr;
}

HRESULT
myUICAHandleCABrowseButton(
    CERTSRVUICASELECTION *pData,
    IN BOOL               fUseDS,
    OPTIONAL IN int       idsPickerTitle,
    OPTIONAL IN int       idsPickerSubTitle,
    OPTIONAL OUT WCHAR   **ppwszSharedFolder)
{
    HRESULT   hr = S_OK;
    PCCRYPTUI_CA_CONTEXT  pCAContext = NULL;
    WCHAR         *pwszSubTitle = NULL;
    WCHAR         *pwszTitle = NULL;
    DWORD          dwCACount;
    WCHAR         *pwszzCAList = NULL;
    WCHAR         *pwszComputer = NULL;
    WCHAR         *pwszTemp = NULL;
    BOOL           fCoInit = FALSE;
    DWORD          dwVersion;

    if (NULL != ppwszSharedFolder)
    {
        *ppwszSharedFolder = NULL;
    }

    if (0 != idsPickerTitle)
    {
        hr = myLoadRCString(pData->hInstance, idsPickerTitle, &pwszTitle);
        if (S_OK != hr)
        {
            pwszTitle = NULL;
            _PrintError(hr, "myLoadRCString");
        }
    }

    if (0 != idsPickerSubTitle)
    {
        hr = myLoadRCString(pData->hInstance, idsPickerSubTitle, &pwszSubTitle);
        if (S_OK != hr)
        {
            pwszSubTitle = NULL;
            _PrintError(hr, "myLoadRCString");
        }
    }

/*
// REMOVED mattt 6/26/00: is this ever wanted: "Browse uses shared folder of machine editbox currently points at"?
// just seems to make changing away from bad machine very very slow

    // get remote shared folder if possible
    hr = myUIGetWindowText(pData->hwndComputerEdit, &pwszComputer);
    _JumpIfError(hr, error, "myUIGetWindowText");

    if (NULL != pwszComputer)
    {
        hr = CoInitialize(NULL);
        if (S_OK != hr && S_FALSE != hr)
        {
            _JumpError(hr, error, "CoInitialize");
        }
        fCoInit = TRUE;
        // get shared folder path on remote machine here
        SetCursor(LoadCursor(NULL, IDC_WAIT));
        hr = myPingCertSrv(pwszComputer, NULL, NULL, &pwszTemp, NULL, NULL, NULL);
        SetCursor(LoadCursor(NULL, IDC_ARROW));
        if (S_OK != hr)
        {
            CSASSERT(NULL == pwszTemp);
            _JumpError(hr, localsharedfolder, "myPingCertSrv");
        }
    }

localsharedfolder:
*/
    hr = myGetConfigFromPicker(
			  pData->hDlg,
			  pwszSubTitle,
			  pwszTitle,
			  pwszTemp,
			  fUseDS,
			  FALSE,	// fCountOnly
			  &dwCACount,
			  &pCAContext);
    if (S_OK != hr && HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
    {
		CSILOG(hr, IDS_ILOG_SELECTCA, NULL, NULL, NULL);
        _JumpError(hr, error, "myGetConfigFromPicker");
    }

    if (S_OK != hr)
		goto done;

	if (NULL == pCAContext)
    {
        CertWarningMessageBox(
            pData->hInstance,
            FALSE,
            pData->hDlg,
            IDS_WRN_CALIST_EMPTY,
            S_OK,
            NULL);
        SetWindowText(pData->hwndCAList, L"");
        SetFocus(pData->hwndComputerEdit);
        SendMessage(pData->hwndComputerEdit, EM_SETSEL, 0, -1);
    }
    else
    {
        CSILOG(hr, IDS_ILOG_SELECTCA, pCAContext->pwszCAMachineName, pCAContext->pwszCAName, NULL);
        
        // update computer
        SendMessage(pData->hwndComputerEdit, WM_SETTEXT,
            0, (LPARAM)pCAContext->pwszCAMachineName);
        
        // construct a single multi string for list update
        DWORD len = wcslen(pCAContext->pwszCAName);
        pwszzCAList = (WCHAR*)LocalAlloc(LMEM_FIXED, (len+2) * sizeof(WCHAR));
        if (NULL == pwszzCAList)
        {
            hr = E_OUTOFMEMORY;
            _JumpError(hr, error, "LocalAlloc");
        }
        wcscpy(pwszzCAList, pCAContext->pwszCAName);
        pwszzCAList[len+1] = '\0';
        
        hr = UICASelectionUpdateCAList(pData->hwndCAList, pwszzCAList);
        _JumpIfError(hr, error, "UICASelectionUpdateCAList");
        LocalFree(pwszzCAList);
        pwszzCAList = NULL;

        // this thread blocks paint message, send it before ping
        UpdateWindow(pData->hDlg);
        
        // ping the computer to see if found a matched ca
        
        if (!fCoInit)
        {
            hr = CoInitialize(NULL);
            if (S_OK != hr && S_FALSE != hr)
            {
                _JumpError(hr, error, "CoInitialize");
            }
            fCoInit = TRUE;
        }
        
        SetCursor(LoadCursor(NULL, IDC_WAIT));
        // ping to get ca list
        hr = myPingCertSrv(
            pCAContext->pwszCAMachineName,
            NULL,
            &pwszzCAList,
            NULL,
            NULL,
            &dwVersion,
            NULL);
        SetCursor(LoadCursor(NULL, IDC_ARROW));
        CSILOG(hr, IDS_ILOG_GETCANAME, pCAContext->pwszCAMachineName, NULL, NULL);
        if (S_OK == hr)
        {
            //bug 262316: don't allow installing Whistler proxy to an older CA
            if(dwVersion<2 && pData->fWebProxySetup)
            {
                hr = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
                // focus on the CA list to trigger a verification of the CA
                SetFocus(pData->hwndCAList);

            } else
            {
            // ping successful
            WCHAR const *pwszPingCA = pwszzCAList;
            
            // go through the list to see if any match
            while (NULL != pwszPingCA && L'\0' != pwszPingCA[0])
            {
                if (0 == wcscmp(pCAContext->pwszCAName, pwszPingCA))
                {
                    // found matched one
                    goto done;
                }
                pwszPingCA += wcslen(pwszPingCA) + 1;
            }
            
            // if we get here, either the CA is offline or the machine is
            // offline and another machine is using the same IP address.
            
            CertWarningMessageBox(
                pData->hInstance,
                FALSE,
                pData->hDlg,
                IDS_WRN_CANAME_NOT_MATCH,
                0,
                NULL);
            // only empty combo edit field
            SetWindowText(pData->hwndCAList, L"");
            SetFocus(pData->hwndCAList);
            }
        }
        else
        {
            // can't ping the ca, selected an estranged ca
            CertWarningMessageBox(
                pData->hInstance,
                FALSE,
                pData->hDlg,
                IDS_WRN_PINGCA_FAIL,
                hr,
                NULL);
            // empty list anyway
            hr = UICASelectionUpdateCAList(pData->hwndCAList, NULL);
            _JumpIfError(hr, error, "UICASelectionUpdateCAList");
            
            SetFocus(pData->hwndComputerEdit);
            SendMessage(pData->hwndComputerEdit, EM_SETSEL, 0, -1);
        }
    }

done:
    hr = myUICAConditionallyDisplayEnterpriseWarning(pData);
    _PrintIfError(hr, "myUICAConditionallyDisplayEnterpriseWarning");

    if (NULL != ppwszSharedFolder)
    {
        *ppwszSharedFolder = pwszTemp;
        pwszTemp = NULL;
    }

    hr = S_OK;
error:
    if (NULL != pwszzCAList)
    {
        LocalFree(pwszzCAList);
    }
    if (NULL != pwszSubTitle)
    {
        LocalFree(pwszSubTitle);
    }
    if (NULL != pwszTitle)
    {
        LocalFree(pwszTitle);
    }
    if (NULL != pwszTemp)
    {
        LocalFree(pwszTemp);
    }
    if (NULL != pwszComputer)
    {
        LocalFree(pwszComputer);
    }
    if (NULL != pCAContext)
    {
        CryptUIDlgFreeCAContext(pCAContext);
    }
    if (fCoInit)
    {
        CoUninitialize();
    }


    return hr;
}

HRESULT
myUICASelectionValidation(
    CERTSRVUICASELECTION *pData,
    BOOL                 *pfValidate)
{
    HRESULT  hr;
    WCHAR   *pwszComputer = NULL;
    WCHAR   *pwszCA = NULL;

    CSASSERT(NULL != pData);

    *pfValidate = FALSE;

    // first, make sure not empty
    hr = myUIGetWindowText(pData->hwndComputerEdit, &pwszComputer);
    _JumpIfError(hr, error, "myUIGetWindowText");

    if (NULL == pwszComputer)
    {
        CertWarningMessageBox(
                pData->hInstance,
                FALSE,
                pData->hDlg,
                IDS_WRN_COMPUTERNAME_EMPTY,
                0,
                NULL);
        SetFocus(pData->hwndComputerEdit);
        goto done;
    }

    hr = myUIGetWindowText(pData->hwndCAList, &pwszCA);
    _JumpIfError(hr, error, "myUIGetWindowText");

    if (NULL == pwszCA)
    {
        CertWarningMessageBox(
                pData->hInstance,
                FALSE,
                pData->hDlg,
                IDS_WRN_CANAME_EMPTY,
                0,
                NULL);
        SetFocus(pData->hwndComputerEdit);
	SendMessage(pData->hwndComputerEdit, EM_SETSEL, 0, -1);
        goto done;
    }

    CSASSERT(pData->CAType != ENUM_UNKNOWN_CA);
    if (pData->CAType == ENUM_UNKNOWN_CA)
    {
         hr = E_UNEXPECTED;
         _JumpIfError(hr, error, "CAType not determined");
    }

    // if hit here
    *pfValidate = TRUE;

done:
    hr = S_OK;
error:
    if (NULL != pwszComputer)
    {
        LocalFree(pwszComputer);
    }
    if (NULL != pwszCA)
    {
        LocalFree(pwszCA);
    }
    return hr;
}