/*++

Copyright (c) 2000, Microsoft Corporation

Module Name:
    eapolutil.c

Abstract:

    Tools and ends


Revision History:

    sachins, Apr 23 2001, Created

--*/

#include "precomp.h"
#pragma hdrstop

//
// EAPOLUI function mapping
//

EAPOLUIFUNCMAP  EapolUIFuncMap[NUM_EAPOL_DLG_MSGS]=
{
    {EAPOLUI_GET_USERIDENTITY, ElGetUserIdentityDlgWorker, ElGetUserIdentityDlgWorker, TRUE, SID_GetUserIdentity},
    {EAPOLUI_GET_USERNAMEPASSWORD, ElGetUserNamePasswordDlgWorker, NULL, TRUE, SID_GetUserNamePassword},
    {EAPOLUI_INVOKEINTERACTIVEUI, ElInvokeInteractiveUIDlgWorker, NULL, TRUE, SID_InvokeInteractiveUI},
    {EAPOLUI_EAP_NOTIFICATION, NULL, NULL, TRUE, 0},
    {EAPOLUI_REAUTHENTICATE, NULL, NULL, FALSE, 0},
    {EAPOLUI_CREATEBALLOON, NULL, NULL, TRUE, SID_AuthenticationFailed},
    {EAPOLUI_CLEANUP, NULL, NULL, FALSE, 0},
    {EAPOLUI_DUMMY, NULL, NULL, FALSE, 0}
};


//
// ElCanShowBalloon
//
// Description:
// Function called by netshell, to query if balloon is to be displayed
//
// Arguments:
//      pGUIDConn - Interface GUID string
//      pszConnectionName - Connection Name
//      pszBalloonText - Pointer to text to be display
//      pszCookie - EAPOL specific information
//
// Return values:
//      S_OK    - Display balloon
//      S_FALSE - Do not display balloon
//

HRESULT 
ElCanShowBalloon ( 
        IN const GUID * pGUIDConn, 
        IN const WCHAR * pszConnectionName,
        IN OUT   BSTR * pszBalloonText, 
        IN OUT   BSTR * pszCookie
        )
{
    EAPOL_EAP_UI_CONTEXT *pEapolUIContext = NULL;
    DWORD               dwIndex = 0;
    DWORD               dwSessionId = 0;
    WCHAR               cwszBuffer[MAX_BALLOON_MSG_LEN];
    WCHAR               wsSSID[MAX_SSID_LEN+1];
    DWORD               dwSizeOfSSID = 0;
    BYTE                *bSSID = NULL;
    WCHAR               *pszFinalBalloonText = NULL;
    DWORD               dwFinalStringId = 0;
    DWORD               dwRetCode = NO_ERROR;
    DWORD               dwRetCode1 = NO_ERROR;
    HRESULT             hr = S_OK;

    do
    {
        pEapolUIContext = (EAPOL_EAP_UI_CONTEXT *)(*pszCookie);

        if (!ProcessIdToSessionId (GetCurrentProcessId (), &dwSessionId))
        {
            dwRetCode = GetLastError ();
            break;
        }

        if (pEapolUIContext->dwSessionId != dwSessionId)
        {
            // Not intended for this session
            dwRetCode = ERROR_INVALID_PARAMETER;
            break;
        }

        dwSizeOfSSID = pEapolUIContext->dwSizeOfSSID;
        bSSID = pEapolUIContext->bSSID;

        for (dwIndex=0; dwIndex < NUM_EAPOL_DLG_MSGS; dwIndex++)
        {
            if (pEapolUIContext->dwEAPOLUIMsgType == 
                    EapolUIFuncMap[dwIndex].dwEAPOLUIMsgType)
            {
                if (EapolUIFuncMap[dwIndex].fShowBalloon)
                {
                    TRACE1 (RPC, "ElCanShowBalloon: Response function found, msg (%ld)",
                            EapolUIContext->dwEAPOLUIMsgType);

                    dwFinalStringId = EapolUIFuncMap[dwIndex].dwStringID;

                    // Verify is balloon indeed needs to be popped up OR
                    // can purpose be achieved without user involvement

                    if (EapolUIFuncMap[dwIndex].EapolUIVerify != NULL)
                    {
                        // Indicate that it is verification cycle, by passing
                        // NULL connection name, to indicate no display !
                        dwRetCode1 = EapolUIFuncMap[dwIndex].EapolUIVerify (
                                    NULL,
                                    pEapolUIContext
                                );
                        if (dwRetCode1 == ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION)
                        {
                            // Continue with show balloon
                            dwRetCode = NO_ERROR;
                        }
                        else
                        {
                            if (dwRetCode1 != NO_ERROR) 
                            {
                                switch (dwRetCode1)
                                {
                                    case ERROR_NO_EAPTLS_CERTIFICATE:
                                    // No certificate found
                                    // Pop balloon accordingly
                                    dwFinalStringId = SID_NoCertificateFound;
                                    // Since we wont take action on this
                                    // balloon being clicked, flag it as
                                    // EAPOLUI_DUMMY
                                    pEapolUIContext->dwEAPOLUIMsgType = EAPOLUI_DUMMY;
                                    dwRetCode = NO_ERROR;
                                    break;

                                    case ERROR_NO_SMART_CARD_READER:
                                    // No smartcard reader found
                                    dwFinalStringId = SID_NoSmartCardReaderFound;
                                    // Since we wont take action on this
                                    // balloon being clicked, flag it as
                                    // EAPOLUI_DUMMY
                                    pEapolUIContext->dwEAPOLUIMsgType = EAPOLUI_DUMMY;
                                    dwRetCode = NO_ERROR;
                                    break;

                                    default:
                                    // Continue with show balloon for any 
                                    // error in verification function
                                    dwRetCode = NO_ERROR;
                                    break;
                                }
                            }
                            else
                            {
                                // No need to process more.
                                // Response has been sent successfully 
                                // without user intervention
                                dwRetCode = ERROR_CAN_NOT_COMPLETE;
                                break;
                            }
                        }
                    }

                    if (dwFinalStringId != 0)
                    {
                        // Load string based on Id
                        if (LoadString (GetModuleHandle(cszModuleName), dwFinalStringId, cwszBuffer, MAX_BALLOON_MSG_LEN) == 0)
                        {
                            dwRetCode = GetLastError ();
                            break;
                        }

                        // Append the network-name / SSID 
                        if (dwSizeOfSSID != 0)
                        {
                            if (0 == MultiByteToWideChar (
                                            CP_ACP,
                                            0,
                                            bSSID,
                                            dwSizeOfSSID,
                                            wsSSID, 
                                            MAX_SSID_LEN+1))
                            {
                                dwRetCode = GetLastError();
                                break;
                            }

                            if ((pszFinalBalloonText = MALLOC ((wcslen(cwszBuffer)+1+ dwSizeOfSSID)*sizeof(WCHAR))) == NULL)
                            {
                                dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
                                break;
                            }
                            wcscpy (pszFinalBalloonText, cwszBuffer);
                            memcpy ((BYTE *)(pszFinalBalloonText + wcslen(cwszBuffer)), (BYTE *)wsSSID, dwSizeOfSSID*sizeof(WCHAR));
                            pszFinalBalloonText[wcslen(cwszBuffer)+dwSizeOfSSID] = L'\0';
                        }
                        else
                        {
                            // Append a "." (period)
                            if ((pszFinalBalloonText = MALLOC ((wcslen(cwszBuffer) + 3)*sizeof(WCHAR))) == NULL)
                            {
                                dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
                                break;
                            }
                            wcscpy (pszFinalBalloonText, cwszBuffer);
                            pszFinalBalloonText[wcslen(cwszBuffer)+1] = L'.';
                            pszFinalBalloonText[wcslen(cwszBuffer)+2] = L'\0';
                        }

                        if (*pszBalloonText)
                        {
                            if (!SysReAllocString (pszBalloonText, pszFinalBalloonText))
                            {
                                dwRetCode = ERROR_CAN_NOT_COMPLETE;
                                break;
                            }
                        }
                        else
                        {
                            *pszBalloonText = SysAllocString (pszFinalBalloonText);
                            if (*pszBalloonText == NULL)
                            {
                                dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
                                break;
                            }
                        }
                    }
                    else
                    {
                        // Display the string that was passed
                    }

                    // If notification message, check to see if explorer 
                    // needs to be started off
                    if (pEapolUIContext->dwEAPOLUIMsgType == EAPOLUI_EAP_NOTIFICATION)
                    {
                        // Parse text message
                        // Attach to cookie
                    }
                }
                else
                {
                    TRACE1 (RPC, "ElCanShowBalloon: No balloon display, msg (%ld)",
                            EapolUIContext.dwEAPOLUIMsgType);
                }
            }
        }

    }
    while (FALSE);

    if (pszFinalBalloonText != NULL)
    {
        FREE (pszFinalBalloonText);
    }

    if (dwRetCode != NO_ERROR)
    {
        hr = S_FALSE;
    }
    return hr;
}


//
// ElOnBalloonClick
//
// Description:
//
// Function called by netshell, in response to a balloon click
//
// Arguments:
//      pGUIDConn - Interface GUID string
//      szCookie - EAPOL specific information
//
// Return values:
//      S_OK    - No error
//      S_FALSE - Error
//

HRESULT 
ElOnBalloonClick ( 
        IN const GUID * pGUIDConn, 
        IN const WCHAR * pszConnectionName,
        IN const BSTR   szCookie
        )
{
    EAPOL_EAP_UI_CONTEXT *pEapolUIContext = NULL;
    DWORD               dwIndex = 0;
    DWORD               dwSessionId = 0;
    DWORD               dwRetCode = NO_ERROR;
    WCHAR               *pwszConnectionName = NULL;
    HRESULT             hr = S_OK;

    do
    {
        pEapolUIContext = (EAPOL_EAP_UI_CONTEXT *)szCookie;
        pwszConnectionName = (WCHAR *)pszConnectionName;

        for (dwIndex=0; dwIndex < NUM_EAPOL_DLG_MSGS; dwIndex++)
        {
            if (pEapolUIContext->dwEAPOLUIMsgType == 
                    EapolUIFuncMap[dwIndex].dwEAPOLUIMsgType)
            {
                if (EapolUIFuncMap[dwIndex].EapolUIFunc)
                {
                    TRACE1 (RPC, "ElOnBalloonClick: Response function found, msg (%ld)",
                            EapolUIContext->dwEAPOLUIMsgType);
                    // Cleanup any previous dialogs for this interface
                    if ((dwRetCode =
                                ElDialogCleanup (
                                    (WCHAR *)pszConnectionName,
                                    szCookie
                                    )) != NO_ERROR)
                    {
                        TRACE0 (RPC, "ElOnBalloonClick: Error in dialog cleanup");
                        break;
                    }

                    if ((dwRetCode = 
                            EapolUIFuncMap[dwIndex].EapolUIFunc (
                                pwszConnectionName,
                                pEapolUIContext
                            )) != NO_ERROR)
                    {
                        TRACE1 (RPC, "ElOnBalloonClick: Response function failed with error %ld",
                                dwRetCode);
                    }
                }
                else
                {
                    TRACE1 (RPC, "ElOnBalloonClick: No response function, msg (%ld)",
                            EapolUIContext.dwEAPOLUIMsgType);
                }
                break;
            }
        }
    }
    while (FALSE);

    hr = HRESULT_FROM_NT (dwRetCode);
    return hr;
}


//
// ElSecureEncodePw
//
// Description:
//
//      Encrypt password locally using user-ACL
//

DWORD
ElSecureEncodePw (
    IN  PWCHAR      *ppwszPassword,
    OUT DATA_BLOB   *pDataBlob
    )
{
    DWORD       dwRetCode = NO_ERROR;
    DATA_BLOB   blobIn, blobOut;

    do
    {
        blobIn.cbData = (wcslen (*ppwszPassword) + 1)*sizeof(WCHAR);
        blobIn.pbData = (BYTE *)*ppwszPassword;

        if (!CryptProtectData (
                    &blobIn,
                    L"",
                    NULL,
                    NULL,
                    NULL,
                    0,
                    &blobOut))
        {
            dwRetCode = GetLastError ();
            break;
        }
        
        // copy over blob to password

        if (pDataBlob->pbData != NULL)
        {
            FREE (pDataBlob->pbData);
            pDataBlob->pbData = NULL;
            pDataBlob->cbData = 0;
        }

        pDataBlob->pbData = MALLOC (blobOut.cbData);
        if (pDataBlob->pbData == NULL)
        {
            dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        memcpy (pDataBlob->pbData, blobOut.pbData, blobOut.cbData);
        pDataBlob->cbData = blobOut.cbData;
    }
    while (FALSE);

    if (blobOut.pbData != NULL)
    {
        LocalFree (blobOut.pbData);
    }

    if (dwRetCode != NO_ERROR)
    {
        if (pDataBlob->pbData != NULL)
        {
            FREE (pDataBlob->pbData);
            pDataBlob->pbData = NULL;
            pDataBlob->cbData = 0;
        }
    }

    return dwRetCode;
}


//
// ElQueryConnectionStatusText
//
// Description:
//
// Function called by netshell, to query appropriate text for 802.1X states
//
// Arguments:
//      pGUIDConn - Interface GUID string
//      ncs - NETCON_STATUS for the interface
//      pszStatusText - Detailed 802.1X status to be displayed
//
// Return values:
//      S_OK    - No error
//      S_FALSE - Error
//

HRESULT 
ElQueryConnectionStatusText ( 
        IN const GUID *  pGUIDConn, 
        IN const NETCON_STATUS ncs,
        IN OUT BSTR *  pszStatusText
        )
{
    WCHAR       wszGuid[GUID_STRING_LEN_WITH_TERM];
    WCHAR       cwszBuffer[MAX_BALLOON_MSG_LEN];
    EAPOL_INTF_STATE    EapolIntfState = {0};
    DWORD       dwStringId = 0;
    DWORD       dwRetCode = NO_ERROR;
    HRESULT     hr = S_OK;

    do
    {
        ZeroMemory ((PVOID)&EapolIntfState, sizeof(EAPOL_INTF_STATE));
        StringFromGUID2 (pGUIDConn, wszGuid, GUID_STRING_LEN_WITH_TERM);

        // Query current EAPOL state
        if ((dwRetCode = WZCEapolQueryState (
                        NULL,
                        wszGuid,
                        &EapolIntfState
                        )) != NO_ERROR)
        {
            break;
        }

        // Assign appropriate display string
        switch (EapolIntfState.dwEapUIState)
        {
            case 0:
                if (EapolIntfState.dwState == EAPOLSTATE_ACQUIRED)
                {
                    dwStringId =  SID_ContactingServer;
                }
                break;
            case EAPUISTATE_WAITING_FOR_IDENTITY:
                dwStringId = SID_AcquiringIdentity;
                break;
            case EAPUISTATE_WAITING_FOR_UI_RESPONSE:
                dwStringId = SID_UserResponse;
                break;
        }

        if (dwStringId != 0)
        {
            if (LoadString (GetModuleHandle(cszModuleName), dwStringId, cwszBuffer, MAX_BALLOON_MSG_LEN) == 0)
            {
                dwRetCode = GetLastError ();
                break;
            }
            if (*pszStatusText)
            {
                if (!SysReAllocString (pszStatusText, cwszBuffer))
                {
                    dwRetCode = ERROR_CAN_NOT_COMPLETE;
                    break;
                }
            }
            else
            {
                *pszStatusText = SysAllocString (cwszBuffer);
                if (*pszStatusText == NULL)
                {
                    dwRetCode = ERROR_NOT_ENOUGH_MEMORY;
                    break;
                }
            }
        }
        else
        {
            // Indicate to netshell that it need not process this response
            hr = S_FALSE;
        }
    }
    while (FALSE);

    if (dwRetCode != NO_ERROR)
        hr = HRESULT_FROM_NT (dwRetCode);

    WZCEapolFreeState (&EapolIntfState);
    return hr;
}