#include "stdafx.h"
#pragma hdrstop

#define PROPERTY_PASSPORTUSER               L"PassportUser"
#define PROPERTY_PASSPORTPASSWORD           L"PassportPassword"
#define PROPERTY_PASSPORTREMEMBERPASSWORD   L"PassportRememberPassword"
#define PROPERTY_PASSPORTUSEMSNEMAIL        L"PassportUseMSNExplorerEmail"
#define PROPERTY_PASSPORTMARSAVAILABLE      L"PassportMSNExplorerAvailable"

// Wizard pages
#define WIZPAGE_WELCOME         0
#define WIZPAGE_FINISH          1
#define WIZPAGE_STARTOFEXT      2    // First webwizard extension page
#define WIZPAGE_MAX             10

#define REGKEY_PASSPORT_INTERNET_SETTINGS     L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Passport"
#define REGVAL_PASSPORT_WIZARDCOMPLETE        L"RegistrationCompleted"
#define REGVAL_PASSPORT_NUMBEROFWIZARDRUNS    L"NumRegistrationRuns"

void BoldControl(HWND hwnd, int id);

class CPassportWizard : public IWizardSite, IServiceProvider, IPassportWizard
{
public:
    CPassportWizard();
    ~CPassportWizard();

    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
    STDMETHOD_(ULONG,AddRef)(void);
    STDMETHOD_(ULONG,Release)(void);

    // IWizardSite
    STDMETHODIMP GetPreviousPage(HPROPSHEETPAGE *phPage);
    STDMETHODIMP GetNextPage(HPROPSHEETPAGE *phPage);
    STDMETHODIMP GetCancelledPage(HPROPSHEETPAGE *phPage)
        { return E_NOTIMPL; }

    // IServiceProvider
    STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv);

    // IPassportWizard
    STDMETHODIMP Show(HWND hwndParent);
    STDMETHODIMP SetOptions(DWORD dwOptions);

protected:
    static CPassportWizard* s_GetPPW(HWND hwnd, UINT uMsg, LPARAM lParam);    

    // Page Procs
    static INT_PTR CALLBACK s_WelcomePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
        { CPassportWizard *ppw = s_GetPPW(hwnd, uMsg, lParam); return ppw->_WelcomePageProc(hwnd, uMsg, wParam, lParam); }
    static INT_PTR CALLBACK s_FinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
        { CPassportWizard *ppw = s_GetPPW(hwnd, uMsg, lParam); return ppw->_FinishPageProc(hwnd, uMsg, wParam, lParam); }

    INT_PTR _WelcomePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    INT_PTR _FinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    HRESULT _CreateMyWebDocumentsLink();
    HRESULT _ApplyChanges(HWND hwnd);
    HRESULT _CreateWizardPages(void);
    HRESULT _SetURLFromNexus();
    HRESULT _GetCurrentPassport();
    HRESULT _LaunchHotmailRegistration();
    BOOL _IsMSNExplorerAvailableForEmail();
    HRESULT _UseMSNExplorerForEmail();

    INT_PTR _WizardNext(HWND hwnd, int iPage);

    LONG _cRef;
    IPropertyBag* _ppb;                         // Property Bag 
    IWebWizardExtension* _pwwe;                 // Wizard host - used for HTML pages
    HPROPSHEETPAGE _rgWizPages[WIZPAGE_MAX];

    DWORD _dwOptions;                           // Option flags for the passport wizard
};

STDAPI CPassportWizard_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
    CPassportWizard *pPPW = new CPassportWizard();
    if (!pPPW)
        return E_OUTOFMEMORY;

    HRESULT hr = pPPW->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
    pPPW->Release();
    return hr;
}

CPassportWizard::CPassportWizard() :
    _cRef(1)
{}

CPassportWizard::~CPassportWizard()
{
    ATOMICRELEASE(_ppb);
    ATOMICRELEASE(_pwwe);
}

// IUnknown
ULONG CPassportWizard::AddRef()
{
    return InterlockedIncrement(&_cRef);
}

ULONG CPassportWizard::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}

HRESULT CPassportWizard::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] = 
    {
        QITABENT(CPassportWizard, IServiceProvider),   // IID_IServiceProvider
        QITABENT(CPassportWizard, IWizardSite),        // IID_IWizardSite
        QITABENT(CPassportWizard, IModalWindow),       // IID_IModalWindow
        QITABENT(CPassportWizard, IPassportWizard),    // IID_IModalWindow
        {0, 0 },
    };
    return QISearch(this, qit, riid, ppv);
}


// IWizardSite
STDMETHODIMP CPassportWizard::GetNextPage(HPROPSHEETPAGE *phPage)
{
    *phPage = _rgWizPages[WIZPAGE_FINISH];
    return S_OK;
}

STDMETHODIMP CPassportWizard::GetPreviousPage(HPROPSHEETPAGE *phPage)
{
    *phPage = _rgWizPages[WIZPAGE_WELCOME];
    return S_OK;
}


// IServiceProvider
STDMETHODIMP CPassportWizard::QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
    HRESULT hr = E_FAIL;
    *ppv = NULL;                // no result yet

    if (guidService == SID_WebWizardHost)
    {
        if (riid == IID_IPropertyBag)
            hr = _ppb->QueryInterface(riid, ppv);
    }

    return hr;
}

// IModalWindow

#define WIZDLG(name, dlgproc, dwFlags)   \
    { MAKEINTRESOURCE(IDD_GETPP_##name##), dlgproc, MAKEINTRESOURCE(IDS_GETPP_HEADER_##name##), MAKEINTRESOURCE(IDS_GETPP_SUBHEADER_##name##), dwFlags }

HRESULT CPassportWizard::_CreateWizardPages(void)
{
    static const WIZPAGE c_wpPages[] =
    {    
        WIZDLG(WELCOME,           CPassportWizard::s_WelcomePageProc,     PSP_HIDEHEADER),
        WIZDLG(FINISH,            CPassportWizard::s_FinishPageProc,      PSP_HIDEHEADER),
    };

    // if we haven't created the pages yet, then lets initialize our array of handlers.

    if (!_rgWizPages[0])
    {
        INITCOMMONCONTROLSEX iccex = { 0 };
        iccex.dwSize = sizeof (iccex);
        iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_PROGRESS_CLASS | ICC_LINK_CLASS;
        InitCommonControlsEx(&iccex);
        LinkWindow_RegisterClass();

        for (int i = 0; i < ARRAYSIZE(c_wpPages) ; i++ )
        {                           
            PROPSHEETPAGE psp = { 0 };
            psp.dwSize = SIZEOF(PROPSHEETPAGE);
            psp.hInstance = g_hinst;
            psp.lParam = (LPARAM)this;
            psp.dwFlags = PSP_USETITLE | PSP_DEFAULT | 
                          PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE |
                          c_wpPages[i].dwFlags;

            psp.pszTemplate = c_wpPages[i].idPage;
            psp.pfnDlgProc = c_wpPages[i].pDlgProc;
            psp.pszTitle = MAKEINTRESOURCE(IDS_GETPP_CAPTION);
            psp.pszHeaderTitle = c_wpPages[i].pHeading;
            psp.pszHeaderSubTitle = c_wpPages[i].pSubHeading;

            _rgWizPages[i] = CreatePropertySheetPage(&psp);
            if (!_rgWizPages[i])
            {
                return E_FAIL;
            }
        }
    }

    return S_OK;
}

HRESULT CPassportWizard::_SetURLFromNexus()
{
    WCHAR szURL[INTERNET_MAX_URL_LENGTH];
    DWORD cch = ARRAYSIZE(szURL) - 1;
    HRESULT hr = PassportGetURL(PASSPORTURL_REGISTRATION, szURL, &cch);
    if (SUCCEEDED(hr))
    {
        hr = _pwwe->SetInitialURL(szURL);
    }
    else
    {
        // Cause the webserviceerror to appear since we can't get a good URL
        hr = _pwwe->SetInitialURL(L"");
    }

    return hr;
}

HRESULT CPassportWizard::Show(HWND hwndParent)
{
    // create our wizard pages, these are required before we do anything
    HRESULT hr = _CreateWizardPages();
    if (SUCCEEDED(hr))
    {
        // we interface with the wizard host via a property bag, so lets create an
        // initialize that before we proceed.
        hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &_ppb));
        if (SUCCEEDED(hr))
        {
            // Provide a property telling Passport if MSN Explorer is available as an e-mail client
            // in the start menu
            SHPropertyBag_WriteBOOL(_ppb, PROPERTY_PASSPORTMARSAVAILABLE, _IsMSNExplorerAvailableForEmail());

            // create the object which will host the HTML wizard pages, these are shown in the frame
            hr = CoCreateInstance(CLSID_WebWizardHost, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IWebWizardExtension, &_pwwe));
            if (SUCCEEDED(hr))
            {
                IUnknown_SetSite(_pwwe, SAFECAST(this, IServiceProvider*));
        
                UINT cExtnPages = 0;
                hr = _pwwe->AddPages(_rgWizPages + WIZPAGE_STARTOFEXT, WIZPAGE_MAX - WIZPAGE_STARTOFEXT, &cExtnPages);
                if (SUCCEEDED(hr))
                {
                    PROPSHEETHEADER psh = { 0 };
                    psh.hwndParent = hwndParent;
                    psh.dwSize = SIZEOF(PROPSHEETHEADER);
                    psh.hInstance = g_hinst;
                    psh.dwFlags = PSH_WIZARD | PSH_WIZARD97 | PSH_STRETCHWATERMARK | PSH_HEADER | PSH_WATERMARK;
                    psh.pszbmHeader = MAKEINTRESOURCE(IDB_GETPP_BANNER);
                    psh.pszbmWatermark = MAKEINTRESOURCE(IDB_GETPP_WATERMARK);
                    psh.phpage = _rgWizPages;
                    psh.nPages = (cExtnPages + WIZPAGE_STARTOFEXT);
                    psh.nStartPage = WIZPAGE_WELCOME;

                    // Return S_FALSE on cancel; otherwise S_OK;
                    hr = PropertySheet(&psh) ? S_OK : S_FALSE;
                }

                IUnknown_SetSite(_pwwe, NULL);
                ATOMICRELEASE(_pwwe);
            }
        }
        ATOMICRELEASE(_ppb);    
    }
    return hr;
}

HRESULT CPassportWizard::SetOptions(DWORD dwOptions)
{
    _dwOptions = dwOptions;
    return S_OK;
}

CPassportWizard* CPassportWizard::s_GetPPW(HWND hwnd, UINT uMsg, LPARAM lParam)
{
    if (uMsg == WM_INITDIALOG)
    {
        PROPSHEETPAGE *ppsp = (PROPSHEETPAGE*)lParam;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, ppsp->lParam);
        return (CPassportWizard*)ppsp->lParam;
    }
    return (CPassportWizard*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}

INT_PTR CPassportWizard::_WizardNext(HWND hwnd, int iPage)
{
    PropSheet_SetCurSel(GetParent(hwnd), _rgWizPages[iPage], -1);
    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
    return TRUE;
}

INT_PTR CPassportWizard::_WelcomePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            {
                SendDlgItemMessage(hwnd, IDC_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0);
                BoldControl(hwnd, IDC_BOLD1);
                // Increment "NumRegistrationRuns" value in the registry
                HKEY hkey;
                if (NO_ERROR == RegCreateKeyEx(HKEY_CURRENT_USER, REGKEY_PASSPORT_INTERNET_SETTINGS, NULL, NULL, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, NULL, &hkey, NULL))
                {
                    DWORD dwType;
                    DWORD nRuns;
                    DWORD cb = sizeof (nRuns);
                    if ((NO_ERROR != RegQueryValueEx(hkey, REGVAL_PASSPORT_NUMBEROFWIZARDRUNS, NULL, &dwType, (LPBYTE) &nRuns, &cb)) ||
                        (REG_DWORD != dwType))
                    {
                        nRuns = 0;
                    }

                    nRuns ++;
                    RegSetValueEx(hkey, REGVAL_PASSPORT_NUMBEROFWIZARDRUNS, NULL, REG_DWORD, (const BYTE *) &nRuns, sizeof (nRuns));
                    RegCloseKey(hkey);
                }
            }
            return TRUE;
        case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;
            switch (pnmh->code)
            {
                case PSN_SETACTIVE:
                    PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_NEXT);
                    return TRUE;
                case PSN_WIZNEXT:
                {
                    // we need ICW to have executed before we navigate to webbased UI
                    LaunchICW();
                    
                    if (SUCCEEDED(_SetURLFromNexus()))
                    {
                        HPROPSHEETPAGE hpageNext;
                        if (SUCCEEDED(_pwwe->GetFirstPage(&hpageNext)))
                        {
                            PropSheet_SetCurSel(GetParent(hwnd), hpageNext, -1);
                        }
                    }

                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) -1);
                    return TRUE;
                }
                case NM_CLICK:
                case NM_RETURN:
                    switch ((int) wParam)
                    {
                        case IDC_PRIVACYLINK:
                            {
                                WCHAR szURL[INTERNET_MAX_URL_LENGTH];
                                DWORD cch = ARRAYSIZE(szURL) - 1;
                                HRESULT hr = PassportGetURL(PASSPORTURL_PRIVACY, szURL, &cch);
                                if (SUCCEEDED(hr))
                                {
                                    WCHAR szURLWithLCID[INTERNET_MAX_URL_LENGTH];
                                    LPCWSTR pszFormat = StrChr(szURL, L'?') ? L"%s&pplcid=%d":L"%s?pplcid=%d";
                                    if (wnsprintf(szURLWithLCID, ARRAYSIZE(szURLWithLCID), pszFormat, szURL, GetUserDefaultLCID()) > 0)
                                    {
                                        // Open the browser to the privacy policy site
                                        SHELLEXECUTEINFO shexinfo = {0};
                                        shexinfo.cbSize = sizeof (shexinfo);
                                        shexinfo.fMask = SEE_MASK_FLAG_NO_UI;
                                        shexinfo.nShow = SW_SHOWNORMAL;
                                        shexinfo.lpFile = szURL;
                                        shexinfo.lpVerb = TEXT("open");
                                        ShellExecuteEx(&shexinfo);
                                    }                                    
                                }                                
                            }
                            return TRUE;
                    }
            }
            return FALSE;
        }
    }
    return FALSE;
}

// Make sure MSN Explorer exists as an email client
BOOL CPassportWizard::_IsMSNExplorerAvailableForEmail()
{
    BOOL fAvailable = FALSE;
    HKEY hkeyMSNEmail;
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Clients\\Mail\\MSN Explorer", 0, KEY_READ, &hkeyMSNEmail))
    {
        fAvailable = TRUE;
        RegCloseKey(hkeyMSNEmail);
    }

    return fAvailable;
}

HRESULT CPassportWizard::_UseMSNExplorerForEmail()
{
    HRESULT hr = E_FAIL;

    if (_IsMSNExplorerAvailableForEmail())
    {
        HKEY hkeyDefaultEmail;
        // Change the default email program for the current user only
        if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\Clients\\Mail", 0, NULL, 0, KEY_SET_VALUE, NULL, &hkeyDefaultEmail, NULL))
        {
            static WCHAR szMSNExplorer[] = L"MSN Explorer";
            if (ERROR_SUCCESS == RegSetValueEx(hkeyDefaultEmail, L"", 0, REG_SZ, (BYTE*) szMSNExplorer, sizeof(szMSNExplorer)))
            {
                hr = S_OK;

                SHSendMessageBroadcast(WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Software\\Clients\\Mail"));
            }

            RegCloseKey(hkeyDefaultEmail);
        }
    }

    return hr;
}

HRESULT CPassportWizard::_ApplyChanges(HWND hwnd)
{
    // Read user, password, and auth DA.
    WCHAR szPassportUser[1024];
    HRESULT hr = SHPropertyBag_ReadStr(_ppb, PROPERTY_PASSPORTUSER, szPassportUser, ARRAYSIZE(szPassportUser));
    if (SUCCEEDED(hr) && *szPassportUser)
    {
        WCHAR szPassportPassword[256];
        hr = SHPropertyBag_ReadStr(_ppb, PROPERTY_PASSPORTPASSWORD, szPassportPassword, ARRAYSIZE(szPassportPassword));
        if (SUCCEEDED(hr) && *szPassportPassword)
        {
            BOOL fRememberPW = SHPropertyBag_ReadBOOLDefRet(_ppb, PROPERTY_PASSPORTREMEMBERPASSWORD, FALSE);
            if (ERROR_SUCCESS == CredUIStoreSSOCredW(NULL, szPassportUser, szPassportPassword, fRememberPW))
            {
                hr = S_OK;

                // Write "RegistrationCompleted" value into the registry
                DWORD dwValue = 1;
                SHSetValue(HKEY_CURRENT_USER, REGKEY_PASSPORT_INTERNET_SETTINGS, REGVAL_PASSPORT_WIZARDCOMPLETE, REG_DWORD, &dwValue, sizeof (dwValue));

#if 0
                if (BST_CHECKED == SendDlgItemMessage(hwnd, IDC_MYWEBDOCUMENTSLINK, BM_GETCHECK, 0, 0))
                {
                    // Temporarily commented out - _CreateMyWebDocumentsLink();
                }

#endif
            }
            else
            {
                hr = E_FAIL;
            }

            if (SHPropertyBag_ReadBOOLDefRet(_ppb, PROPERTY_PASSPORTUSEMSNEMAIL, FALSE))
            {
                _UseMSNExplorerForEmail();
            }
        }
    }

    return hr;
}

INT_PTR CPassportWizard::_FinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            SendDlgItemMessage(hwnd, IDC_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0);
            return TRUE;

        case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;
            switch (pnmh->code)
            {
                case PSN_SETACTIVE:
                {
                    // Temporarily commented out - SendDlgItemMessage(hwnd, IDC_MYWEBDOCUMENTSLINK, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);

                    WCHAR szPassportUser[1024];

                    // Try to get the passport user name... we may have to add an error page if this fails... TODO
                    HRESULT hr = SHPropertyBag_ReadStr(_ppb, PROPERTY_PASSPORTUSER, szPassportUser, ARRAYSIZE(szPassportUser));
                    if (SUCCEEDED(hr) && *szPassportUser)
                    {
                        SetDlgItemText(hwnd, IDC_YOURPASSPORT, szPassportUser);
                    }

                    PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_FINISH);
                    return TRUE;
                }

                case PSN_WIZBACK:
                    // The next page is the web wizard host. We show different pages depending whether or not
                    // the user has an email account (or passport) or not.
                    if (SUCCEEDED(_SetURLFromNexus()))
                    {
                        HPROPSHEETPAGE hpageNext;
                        if (SUCCEEDED(_pwwe->GetFirstPage(&hpageNext)))
                        {
                            PropSheet_SetCurSel(GetParent(hwnd), hpageNext, -1);
                        }
                    }

                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
                    return TRUE;

                case PSN_WIZFINISH:
                    _ApplyChanges(hwnd);
                    return TRUE;
            }
            break;
        }
    }
    return FALSE;
}

// Help requires a rundll entrypoint to run passport wizard
void APIENTRY PassportWizardRunDll(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
{
    HRESULT hr = CoInitialize(NULL);
    if (SUCCEEDED(hr))
    {
        IPassportWizard* pPW = NULL;
        hr = CoCreateInstance(CLSID_PassportWizard, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPassportWizard, &pPW));
        if (SUCCEEDED(hr))
        {
            pPW->SetOptions(PPW_LAUNCHEDBYUSER);
            pPW->Show(hwndStub);
            pPW->Release();
        }

        CoUninitialize();
    }
}

void BoldControl(HWND hwnd, int id)
{
    HWND hwndTitle = GetDlgItem(hwnd, id);

    // Get the existing font
    HFONT hfontOld = (HFONT) SendMessage(hwndTitle, WM_GETFONT, 0, 0);

    LOGFONT lf = {0};
    if (GetObject(hfontOld, sizeof(lf), &lf))
    {
        lf.lfWeight = FW_BOLD;

        HFONT hfontNew = CreateFontIndirect(&lf);
        if (hfontNew)
        {
            SendMessage(hwndTitle, WM_SETFONT, (WPARAM) hfontNew, FALSE);

            // Don't do this, its shared.
            // DeleteObject(hfontOld);
        }
    }
}