// MainPage.cpp : Implementation of CMainPage
#include "stdafx.h"
#include "MainPage.h"

EXTERN_C const CLSID CLSID_MainPage = __uuidof(CMainPage);

/////////////////////////////////////////////////////////////////////////////
// CMainPage

LPWSTR CMainPage::c_aHTML[] =
{
    L"res://nusrmgr.exe/mainpage.htm",
    L"res://nusrmgr.exe/mainpage_sec.htm"
};

STDMETHODIMP CMainPage::createUserTable(IDispatch *pdispUserTableParent)
{
    HRESULT hr;

    if (NULL == pdispUserTableParent)
        return E_INVALIDARG;

    if (NULL == _pBag)
        return E_UNEXPECTED;

    CComVariant var;
    hr = _pBag->Read(UA_PROP_USERLIST, &var, NULL);
    if (SUCCEEDED(hr))
    {
        hr = E_FAIL;

        CComQIPtr<ILogonEnumUsers> spUserList(var.punkVal);

        CComQIPtr<IHTMLElement> spParent(pdispUserTableParent);
        if (spParent)
        {
            CComBSTR strHTML;
            hr = CreateUserTableHTML(spUserList, 2, &strHTML);
            if (SUCCEEDED(hr))
                hr = spParent->put_innerHTML(strHTML);
        }
    }
    return hr;
}


LPWSTR FormatString(LPCWSTR pszFormat, ...)
{
    LPWSTR pszResult = NULL;
    DWORD dwResult;
    va_list args;

    if (NULL == pszFormat)
        return NULL;

    va_start(args, pszFormat);
    dwResult = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
                              pszFormat,
                              0,
                              0,
                              (LPWSTR)&pszResult,
                              1,
                              &args);
    va_end(args);

    if (0 == dwResult && NULL != pszResult)
    {
        LocalFree(pszResult);
        pszResult = NULL;
    }

    return pszResult;
}

//
// Localized strings (move to resources)
//
const WCHAR L_Admin_Property[]          = L"Owner account";
const WCHAR L_Standard_Property[]       = L"Standard account";
const WCHAR L_Limited_Property[]        = L"Limited account";
const WCHAR L_Password_Property[]       = L"<BR>Password-protected";
const WCHAR L_GuestEnabled_Property[]   = L"Guest access allowed";
const WCHAR L_GuestDisabled_Property[]  = L"Guest access not allowed";

const WCHAR L_Account_ToolTip[]         = L"Changes this person&#039s account information, such as the account type, name, or picture, or lets you delete this account.";
const WCHAR L_Guest_ToolTip[]           = L"Lets you change the guest account picture or prevent guest access to this computer.";
const WCHAR L_GuestEnable_ToolTip[]     = L"Provides computer access for people without a user account on this machine.";


//
// Non-localized strings
//
const WCHAR c_szAdminGroup[]            = L"Owners";
const WCHAR c_szStandardGroup[]         = L"Adults";
const WCHAR c_szLimitedGroup[]          = L"Children";
const WCHAR c_szGuestGroup[]            = L"Guests";
const WCHAR c_szDefaultImage[]          = L"accountgrey.bmp";


struct { LPCWSTR szGroupName; LPCWSTR szAccountType; }
g_AccountTypes[] =
{
    { c_szAdminGroup,       L_Admin_Property        },
    { c_szStandardGroup,    L_Standard_Property     },
    { c_szLimitedGroup,     L_Limited_Property      },
    { c_szGuestGroup,       L_GuestEnabled_Property },
};

BOOL IsAccountType(ILogonUser* pUser, UINT iType)
{
    if (iType < ARRAYSIZE(g_AccountTypes))
    {
        CComVariant varType;
        if (SUCCEEDED(pUser->get_setting(L"AccountType", &varType)) && VT_BSTR == varType.vt)
        {
            return (0 == lstrcmpiW(varType.bstrVal, g_AccountTypes[iType].szGroupName));
        }
    }
    return FALSE;
}

UINT GetAccountType(ILogonUser* pUser)
{
    UINT i;
    for (i = 0; i < ARRAYSIZE(g_AccountTypes); i++)
    {
        if (IsAccountType(pUser, i))
            return i;
    }
    return 2; //LIMITED;
}

BOOL IsSameAccount(ILogonUser* pUser, LPCWSTR pszLoginName)
{
    CComVariant varName;
    if (SUCCEEDED(pUser->get_setting(L"LoginName", &varName)) && VT_BSTR == varName.vt)
    {
        return (0 == lstrcmpiW(varName.bstrVal, pszLoginName));
    }
    return FALSE;
}

BSTR GetUserDisplayName(ILogonUser* pUser)
{
    BSTR strResult = NULL;

    CComVariant var;
    HRESULT hr = pUser->get_setting(L"DisplayName", &var);
    if (FAILED(hr) || VT_BSTR != var.vt || NULL == var.bstrVal || L'\0' == *var.bstrVal)
    {
        var.Clear();
        hr = pUser->get_setting(L"LoginName", &var);
    }

    if (SUCCEEDED(hr) && VT_BSTR == var.vt && NULL != var.bstrVal)
    {
        strResult = var.bstrVal;
        var.vt = VT_EMPTY;

        // Truncate really long names
        if (lstrlenW(strResult) > 20)
        {
            //var iBreak = szDisplayName.lastIndexOf(' ',17);
            //if (-1 == iBreak) iBreak = 17;
            //szDisplayName = szDisplayName.substring(0,iBreak) + "...";
            lstrcpyW(&strResult[17], L"...");
        }
    }

    return strResult;
}

LPWSTR CreateUserDisplayHTML(LPCWSTR pszName, LPCWSTR pszSubtitle, LPCWSTR pszPicture)
{
    static const WCHAR c_szUserHTML[] =
        L"<TABLE cellspacing=0 cellpadding=0 style='border:0'><TR>" \
          L"<TD style='padding:1mm;margin-right:2mm'>" \
            L"<IMG src='%3' class='UserPict' onerror='OnPictureLoadError(this);'/>" \
          L"</TD>" \
          L"<TD style='vertical-align:middle'>" \
            L"<DIV class='FontSubHeader1 ColorPrimaryLink1'>%1</DIV>" \
            L"<DIV class='FontDescription1 ColorPrimaryLink1'>%2</DIV>" \
          L"</TD>" \
        L"</TR></TABLE>";

    return FormatString(c_szUserHTML, pszName, pszSubtitle, pszPicture);
}

LPWSTR CreateUserDisplayHTML(ILogonUser* pUser)
{
    if (NULL == pUser)
        return NULL;

    CComBSTR strSubtitle(g_AccountTypes[GetAccountType(pUser)].szAccountType);

    VARIANT_BOOL bPassword = VARIANT_FALSE;
    if (SUCCEEDED(pUser->get_passwordRequired(&bPassword)) && (VARIANT_TRUE == bPassword))
    {
        strSubtitle.Append(L_Password_Property);
    }

    CComVariant varPicture;
    if (FAILED(pUser->get_setting(L"Picture", &varPicture)))
        varPicture = c_szDefaultImage;

    CComBSTR strName;
    strName.Attach(GetUserDisplayName(pUser));

    return CreateUserDisplayHTML(strName, strSubtitle, varPicture.bstrVal);
}

LPWSTR CreateDisabledGuestHTML()
{
    return CreateUserDisplayHTML(g_szGuestName, L_GuestDisabled_Property, c_szDefaultImage);
}

HRESULT CreateUserTableHTML(ILogonEnumUsers* pUserList, UINT cColumns, BSTR* pstrHTML)
{
    HRESULT hr;

    static const WCHAR c_szTableStart[] = L"<TABLE cellspacing=10 cellpadding=1 style='border:0;table-layout:fixed'>";
    static const WCHAR c_szTableEnd[] = L"</TABLE>";
    static const WCHAR c_szTRStart[] = L"<TR>";
    static const WCHAR c_szTREnd[] = L"</TR>";
    static const WCHAR c_szTDStart[] = L"<TD class='Selectable' tabindex=0 LoginName='%1' title='%2' onclick='%3' style='width:%4!d!%%'>";
    static const WCHAR c_szTDEnd[] = L"</TD>";
    static const WCHAR c_szSwitchUser[] = L"window.external.navigate(\"{F4924514-CFBC-4AAB-9EC5-6C6E6D0DB38D}\",false,this.LoginName);";
    static const WCHAR c_szEnableGuest[] = L"window.external.navigate(\"enableguest.htm\",false);";

    if (NULL == pUserList)
        return E_INVALIDARG;

    if (NULL == pstrHTML)
        return E_POINTER;

    *pstrHTML = NULL;

    if (0 == cColumns)
        cColumns = 1;

    LONG nColWidth = 100 / cColumns; // percent (e.g. for 2 columns, width=50%)

    UINT cUsers = 0;
    hr = pUserList->get_length(&cUsers);
    if (SUCCEEDED(hr) && 0 == cUsers)
        hr = E_FAIL;
    if (FAILED(hr))
        return hr;

    CComPtr<IStream> spStream;
    hr = CreateStreamOnHGlobal(NULL, TRUE, &spStream);
    if (FAILED(hr))
        return hr;

    // Continue if this fails
    CComPtr<ILogonUser> spLoggedOnUser;
    pUserList->get_currentUser(&spLoggedOnUser);

    BOOL fIncludeSelf = (NULL != spLoggedOnUser.p);
    BOOL bShowAdmin = (!fIncludeSelf || IsSameAccount(spLoggedOnUser, g_szAdminName));
    if (!bShowAdmin)
    {
        // TODO: Check registry
    }

    UINT i;
    UINT j = 0;

    VARIANT varI;
    varI.vt = VT_I4;

    ULONG cbWritten;
    spStream->Write(c_szTableStart, sizeof(c_szTableStart)-sizeof(L'\0'), &cbWritten);

    for (i = 0; i < cUsers;)
    {
        spStream->Write(c_szTRStart, sizeof(c_szTRStart)-sizeof(L'\0'), &cbWritten);

        for (j = 0; j < cColumns && i < cUsers; i++)
        {
            CComPtr<ILogonUser> spUser;
            varI.ulVal = i;
            hr = pUserList->item(varI, &spUser);
            if (FAILED(hr))
                continue;

            CComVariant varLoginName;
            if (FAILED(spUser->get_setting(L"LoginName", &varLoginName)) || VT_BSTR != varLoginName.vt)
                continue;

            // Add "Guest" later
            if (0 == lstrcmpiW(varLoginName.bstrVal, g_szGuestName))
                continue;

            // Normally don't want to show "Administrator"
            if (!bShowAdmin && (0 == lstrcmpiW(varLoginName.bstrVal, g_szAdminName)))
                continue;

            BOOL bIsLoggedOnUser = spLoggedOnUser ? IsSameAccount(spLoggedOnUser, varLoginName.bstrVal) : FALSE;

            //
            // fIncludeSelf causes spLoggedOnUser to be
            // placed first in the list
            //
            if (fIncludeSelf || !bIsLoggedOnUser)
            {
                if (fIncludeSelf)
                {
                    if (!bIsLoggedOnUser)
                    {
                        CComVariant varTemp;
                        if (SUCCEEDED(spLoggedOnUser->get_setting(L"LoginName", &varTemp)) && VT_BSTR == varTemp.vt)
                        {
                            varLoginName = varTemp;
                            spUser = spLoggedOnUser;
                            i--;
                        }
                    }
                    fIncludeSelf = FALSE;
                }

                LPWSTR pszTDStart = FormatString(c_szTDStart, varLoginName.bstrVal, L_Account_ToolTip, c_szSwitchUser, nColWidth);
                if (NULL != pszTDStart)
                {
                    spStream->Write(pszTDStart, sizeof(WCHAR)*lstrlenW(pszTDStart), &cbWritten);
                    LocalFree(pszTDStart);
                }
                else
                {
                    continue;
                }

                LPWSTR pszUserDisplay = CreateUserDisplayHTML(spUser);
                if (NULL != pszUserDisplay)
                {
                    spStream->Write(pszUserDisplay, sizeof(WCHAR)*lstrlenW(pszUserDisplay), &cbWritten);
                    LocalFree(pszUserDisplay);
                }

                spStream->Write(c_szTDEnd, sizeof(c_szTDEnd)-sizeof(L'\0'), &cbWritten);

                j++;
            }
        }

        // Last time through?
        if (i == cUsers)
        {
            // Add the "Guest" entry now

            VARIANT_BOOL bGuestEnabled = VARIANT_FALSE;
            CComPtr<ILocalMachine> spLocalMachine;
            hr = spLocalMachine.CoCreateInstance(CLSID_ShellLocalMachine);
            if (SUCCEEDED(hr))
                spLocalMachine->get_isGuestEnabled(&bGuestEnabled);

            if (j == cColumns)
            {
                spStream->Write(c_szTREnd, sizeof(c_szTREnd)-sizeof(L'\0'), &cbWritten);
                spStream->Write(c_szTRStart, sizeof(c_szTRStart)-sizeof(L'\0'), &cbWritten);
            }

            LPCWSTR pszTitle;
            LPCWSTR pszOnClick;
            LPWSTR pszUserDisplay;

            if (VARIANT_TRUE == bGuestEnabled)
            {
                // Enabled Guest is a real entry (from pUserList)

                CComPtr<ILogonUser> spUser;
                CComVariant var(g_szGuestName);
                hr = pUserList->item(var, &spUser);

                pszTitle = L_Guest_ToolTip;
                pszOnClick = c_szSwitchUser;
                pszUserDisplay = CreateUserDisplayHTML(spUser);
            }
            else
            {
                // Disabled Guest is a fake entry

                pszTitle = L_GuestEnable_ToolTip;
                pszOnClick = c_szEnableGuest;
                pszUserDisplay = CreateDisabledGuestHTML();
            }

            LPWSTR pszTDStart = FormatString(c_szTDStart, g_szGuestName, pszTitle, pszOnClick, nColWidth);
            if (NULL != pszTDStart)
            {
                spStream->Write(pszTDStart, sizeof(WCHAR)*lstrlenW(pszTDStart), &cbWritten);
                LocalFree(pszTDStart);

                if (NULL != pszUserDisplay)
                    spStream->Write(pszUserDisplay, sizeof(WCHAR)*lstrlenW(pszUserDisplay), &cbWritten);

                spStream->Write(c_szTDEnd, sizeof(c_szTDEnd)-sizeof(L'\0'), &cbWritten);
            }

            if (NULL != pszUserDisplay)
                LocalFree(pszUserDisplay);
        }

        spStream->Write(c_szTREnd, sizeof(c_szTREnd)-sizeof(L'\0'), &cbWritten);
    }

    // Include the NULL terminator on the last write
    spStream->Write(c_szTableEnd, sizeof(c_szTableEnd), &cbWritten);

    HGLOBAL hBuffer;
    hr = GetHGlobalFromStream(spStream, &hBuffer);
    if (SUCCEEDED(hr))
    {
        LPCWSTR pszHTML = (LPCWSTR)GlobalLock(hBuffer);
        *pstrHTML = SysAllocString(pszHTML);
        GlobalUnlock(hBuffer);
        if (NULL == *pstrHTML)
            hr = E_OUTOFMEMORY;
    }

    return hr;
}