/*
 * network - Dialog box property sheet for "network goo"
 */

#include "tweakui.h"

#pragma BEGIN_CONST_DATA

#define c_tszShutdownWithoutLogon TEXT("ShutdownWithoutLogon")

KL const c_klAutoLogon = { &g_hkLMSMWNTCV, c_tszWinlogon, c_tszAutoLogon };
KL const c_klDefUser = { &g_hkLMSMWNTCV, c_tszWinlogon, c_tszDefaultUserName };
KL const c_klDefPass = { &g_hkLMSMWNTCV, c_tszWinlogon, c_tszDefaultPassword };
KL const c_klShutdown = { &g_hkLMSMWNTCV, c_tszWinlogon, c_tszShutdownWithoutLogon };
KL const c_klRunServ = { &g_hkLMSMWCV, c_tszRunServices, g_tszName };

const static DWORD CODESEG rgdwHelp[] = {
        IDC_LOGONAUTO,      IDH_AUTOLOGON,
        IDC_LOGONUSERTXT,   IDH_AUTOLOGONUSER,
        IDC_LOGONUSER,      IDH_AUTOLOGONUSER,
        IDC_LOGONPASSTXT,   IDH_AUTOLOGONPASS,
        IDC_LOGONPASS,      IDH_AUTOLOGONPASS,
        IDC_LOGONSHUTDOWN,  IDH_LOGONSHUTDOWN,
        0,                  0,
};

/*
 * Instanced.  We're a cpl so have only one instance, but I declare
 * all the instance stuff in one place so it's easy to convert this
 * code to multiple-instance if ever we need to.
 *
 * Note: All our instance data is kept in the dialog itself; don't need this
 */
#if 0
typedef struct NDII {           /* network dialog instance info */
} NDII, *PNDII;

NDII ndii;
#define pndii (&ndii)
#endif

/* #define TAMMEB_PERF */
#ifdef TAMMEB_PERF

/*****************************************************************************
 *
 *  Network_LogQPC
 *
 *      Log the current time in "shell perf time", which is basically
 *      milliseconds, but using QPC instead of GetTickCount().
 *
 *****************************************************************************/

void
Network_LogQPC(LPCTSTR pszName)
{
    /* For perf testing, log the QPC time when we hit OK. */
    {
        TCHAR tszTemp[40];
        LARGE_INTEGER cur, freq;
        QueryPerformanceFrequency(&freq);
        QueryPerformanceCounter(&cur);
        wsprintf(tszTemp, TEXT("%d"), (DWORD)((cur.QuadPart * 1000) / freq.QuadPart));
        WritePrivateProfileString(TEXT("NetworkLogon"), pszName, tszTemp, TEXT("C:\\TWEAKUI.LOG"));
    }
}
#endif

/*****************************************************************************
 *
 *  Network_SetDirty
 *
 *      Make a control dirty.
 *
 *****************************************************************************/

#define Network_SetDirty    Common_SetDirty

/*****************************************************************************
 *
 *  Network_PklToDlgItemText
 *
 *      Read dialog item text from the registry.
 *
 *****************************************************************************/

#define ctchDlgItem 256

void PASCAL
Network_PklToDlgItemText(HWND hdlg, UINT idc, PKL pkl)
{
    TCHAR tsz[ctchDlgItem];

    GetStrPkl(tsz, cbX(tsz), pkl);
    SetDlgItemTextLimit(hdlg, idc, tsz, cA(tsz));
}

/*****************************************************************************
 *
 *  Network_DlgItemTextToPkl
 *
 *      Copy dialog item text to the registry.
 *
 *****************************************************************************/

void PASCAL
Network_DlgItemTextToPkl(HWND hdlg, UINT idc, PKL pkl)
{
    TCHAR tsz[ctchDlgItem];

    GetDlgItemText(hdlg, idc, tsz, cA(tsz));
    if (tsz[0]) {
        SetStrPkl(pkl, tsz);
    } else {
        DelPkl(pkl);
    }
}

/*****************************************************************************
 *
 *  Network_Reset
 *
 *      Reset all controls to initial values.  This also marks
 *      the control as clean.
 *
 *****************************************************************************/

BOOL PASCAL
Network_Reset(HWND hdlg)
{
    CheckDlgButton(hdlg, IDC_LOGONAUTO, GetIntPkl(0, &c_klAutoLogon));

    Network_PklToDlgItemText(hdlg, IDC_LOGONUSER, &c_klDefUser);

    if (g_fNT5) {
        WCHAR ubuf[ctchDlgItem];
        ubuf[0] = TEXT('\0');
        GetSecretDefaultPassword(ubuf, ctchDlgItem);
        SetDlgItemTextW(hdlg, IDC_LOGONPASS, ubuf);
        SendDlgItemMessageW(hdlg, IDC_LOGONPASS, EM_LIMITTEXT, cA(ubuf) - 1, 0);
        CheckDlgButton(hdlg, IDC_LOGONSHUTDOWN, GetIntPkl(0, &c_klShutdown));
    } else {
        Network_PklToDlgItemText(hdlg, IDC_LOGONPASS, &c_klDefPass);
        DestroyWindow(GetDlgItem(hdlg, IDC_LOGONSHUTDOWN));
    }

    Common_SetClean(hdlg);

    return 1;
}



/*****************************************************************************
 *
 *  Network_Apply
 *
 *      Write the changes to the registry.
 *
 *****************************************************************************/

BOOL NEAR PASCAL
Network_Apply(HWND hdlg)
{
    BOOL fAuto;

    Network_DlgItemTextToPkl(hdlg, IDC_LOGONUSER, &c_klDefUser);

    if (g_fNT5) {
        WCHAR ubuf[ctchDlgItem];
        GetDlgItemTextW(hdlg, IDC_LOGONPASS, ubuf, cA(ubuf));
        SetSecretDefaultPassword(ubuf);
        DelPkl(&c_klDefPass);           /* And delete the unsafe one */
        SetIntPkl(IsDlgButtonChecked(hdlg, IDC_LOGONSHUTDOWN), &c_klShutdown);
    } else {
        Network_DlgItemTextToPkl(hdlg, IDC_LOGONPASS, &c_klDefPass);
    }

    fAuto = IsDlgButtonChecked(hdlg, IDC_LOGONAUTO);
    if (fAuto) {
        SetIntPkl(fAuto, &c_klAutoLogon);
    } else {
        DelPkl(&c_klAutoLogon);
    }

    /*
     *  NT does this automatically, so we need to be hacky only on Win95a.
     */
    if (!g_fNT) {
        if (fAuto) {
            SetStrPkl(&c_klRunServ, c_tszFixAutoLogon);
        } else {
            DelPkl(&c_klRunServ);
        }
    }

    return Network_Reset(hdlg);
}

/*****************************************************************************
 *
 *  Network_FactoryReset
 *
 *      Autologon = false
 *      AutoUser = none
 *      AutoPass = none
 *
 *****************************************************************************/

BOOL PASCAL
Network_FactoryReset(HWND hdlg)
{
    Network_SetDirty(hdlg);

    CheckDlgButton(hdlg, IDC_LOGONAUTO, FALSE);
    SetDlgItemText(hdlg, IDC_LOGONUSER, c_tszNil);
    SetDlgItemText(hdlg, IDC_LOGONPASS, c_tszNil);
    return 1;
}

/*****************************************************************************
 *
 *  Network_OnCommand
 *
 *      Ooh, we got a command.
 *
 *****************************************************************************/

BOOL PASCAL
Network_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
    switch (id) {
    case IDC_RESET:     /* Reset to factory default */
        if (codeNotify == BN_CLICKED) return Network_FactoryReset(hdlg);
        break;

    case IDC_LOGONAUTO:
    case IDC_LOGONSHUTDOWN:
        if (codeNotify == BN_CLICKED) Network_SetDirty(hdlg);
        break;

    case IDC_LOGONUSER:
    case IDC_LOGONPASS:
        if (codeNotify == EN_CHANGE) Network_SetDirty(hdlg);
        break;

    }
    return 0;
}

/*****************************************************************************
 *
 *  Network_OnNotify
 *
 *      Ooh, we got a notification.
 *
 *****************************************************************************/

BOOL PASCAL
Network_OnNotify(HWND hdlg, NMHDR FAR *pnm)
{
    switch (pnm->code) {
    case PSN_APPLY:
        Network_Apply(hdlg);
        break;

    }
    return 0;
}

/*****************************************************************************
 *
 *  Network_OnInitDialog
 *
 *      Initialize the controls.
 *
 *****************************************************************************/

BOOL INLINE
Network_OnInitDialog(HWND hdlg)
{
    if (g_fNT5) {
        DestroyWindow(GetDlgItem(hdlg, IDC_LOGONPASSUNSAFE));
    }
    return Network_Reset(hdlg);
}

/*****************************************************************************
 *
 *  Our window procedure.
 *
 *****************************************************************************/

/*
 * The HANDLE_WM_* macros weren't designed to be used from a dialog
 * proc, so we need to handle the messages manually.  (But carefully.)
 */

INT_PTR EXPORT
Network_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
    switch (wm) {
    case WM_INITDIALOG: return Network_OnInitDialog(hdlg);

    case WM_COMMAND:
        return Network_OnCommand(hdlg,
                               (int)GET_WM_COMMAND_ID(wParam, lParam),
                               (UINT)GET_WM_COMMAND_CMD(wParam, lParam));
    case WM_NOTIFY:
        return Network_OnNotify(hdlg, (NMHDR FAR *)lParam);

    case WM_HELP: Common_OnHelp(lParam, &rgdwHelp[0]); break;

    case WM_CONTEXTMENU: Common_OnContextMenu(wParam, &rgdwHelp[0]); break;

    default: return 0;  /* Unhandled */
    }
    return 1;           /* Handled */
}

/*****************************************************************************
 *
 *  GetClassAtom
 *
 *****************************************************************************/

WORD PASCAL
GetClassAtom(HWND hwnd)
{
    return GetClassWord(hwnd, GCW_ATOM);
}

/*****************************************************************************
 *
 *  Network_FindVictim
 *
 *  Look arund to see if there is a window that meets the following
 *  criteria:
 *
 *      1. Is a dialog box.
 *      2. Contains two edit controls, one of which is password-protected.
 *
 *  If so, then the hwnd list is filled in with the two edit controls.
 *
 *****************************************************************************/

typedef struct AUTOLOGON {
    TCHAR tszUser[ctchDlgItem];
    TCHAR tszPass[ctchDlgItem];
} AUTOLOGON, *PAUTOLOGON;


#define GetWindowClass(hwnd)    GetClassWord(hwnd, GCW_ATOM)

HWND PASCAL
Network_FindVictim(HWND hwndEdit, HWND rghwnd[])
{
    WORD atmEdit = GetClassAtom(hwndEdit);
    HWND hdlg;

    for (hdlg = GetWindow(GetDesktopWindow(), GW_CHILD); hdlg;
         hdlg = GetWindow(hdlg, GW_HWNDNEXT)) {

        /*
         *  If we have a dialog box, study it.
         */
        if (GetClassAtom(hdlg) == 0x8002) {
            HWND hwnd, hwndUser = 0;
            for (hwnd = GetWindow(hdlg, GW_CHILD); hwnd;
                 hwnd = GetWindow(hwnd, GW_HWNDNEXT)) {

                /*
                 *  We care only about visible non-read-only edit controls.
                 */
                if (GetClassAtom(hwnd) == atmEdit) {
                    LONG ws = GetWindowLong(hwnd, GWL_STYLE);
                    if (!(ws & ES_READONLY) && (ws & WS_VISIBLE)) {
                        /*
                         *  If we haven't found a "user name",
                         *  then the first edit we find had better
                         *  not be password-protected.  If it is,
                         *  then we punt, because we're confused.
                         *
                         *  If we have found a "user name", then the
                         *  next edit we find had better be
                         *  password-protected.
                         */
                        if (hwndUser == 0) {
                            if (ws & ES_PASSWORD) goto nextdialog;
                            hwndUser = hwnd;
                        } else {
                            if (!(ws & ES_PASSWORD)) goto nextdialog;
                            rghwnd[0] = hwndUser;
                            rghwnd[1] = hwnd;
                            return hdlg;
                        }
                    }
                }
            }
        }
    nextdialog:;
    }
    return 0;
}

/*****************************************************************************
 *
 *  Network_ForceString
 *
 *  Force a string into an edit control.  We cannot use SetWindowText
 *  because that doesn't work inter-thread.
 *
 *****************************************************************************/

void PASCAL
Network_ForceString(HWND hwnd, LPCTSTR ptsz)
{
    Edit_SetSel(hwnd, 0, -1);
    FORWARD_WM_CLEAR(hwnd, SendMessage);

    for (; *ptsz && IsWindow(hwnd); ptsz++) {
        SendMessage(hwnd, WM_CHAR, *ptsz, 0L);
    }
}

/*****************************************************************************
 *
 *  Network_Snoop
 *
 *  Look to see if we have a winner.  The shift key suppresses autologon.
 *
 *****************************************************************************/

void PASCAL
Network_Snoop(HWND hwnd)
{
    HWND rghwnd[2];
    HWND hdlg;

    hdlg = Network_FindVictim(hwnd, rghwnd);
    if (hdlg && GetAsyncKeyState(VK_SHIFT) >= 0) {
        PAUTOLOGON pal = (PAUTOLOGON)GetWindowLongPtr(hwnd, GWLP_USERDATA);
        if (pal) {
            Network_ForceString(rghwnd[0], pal->tszUser);
            Network_ForceString(rghwnd[1], pal->tszPass);
            FORWARD_WM_COMMAND(hdlg, IDOK, GetDlgItem(hdlg, IDOK),
                               BN_CLICKED, PostMessage);
            PostMessage(hwnd, WM_CLOSE, 0, 0L);
#ifdef TAMMEB_PERF
            Network_LogQPC(TEXT("SuccessQPC"));
#endif
        }
    }
}

/*****************************************************************************
 *
 *  Network_WndProc
 *
 *  Window procedure for our "Keep an eye on the logon process".
 *
 *  When the timer fires, we nuke ourselves, under the assumption that the
 *  network dialog box ain't a-comin' so there's no point a-waitin' fer it.
 *
 *****************************************************************************/

LRESULT CALLBACK
Network_WndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    switch (wm) {

    case WM_DESTROY:
        KillTimer(hwnd, 1);
        PostQuitMessage(0);
        break;

    case WM_TIMER:
    case WM_KILLFOCUS:
        Network_Snoop(hwnd);
        PostMessage(hwnd, WM_CLOSE, 0, 0L);
        break;

    }

    return DefWindowProc(hwnd, wm, wp, lp);
}

/*****************************************************************************
 *
 *  TweakLogon
 *
 *  Rundll entry point for automatic logon.  This is run as a system service.
 *
 *****************************************************************************/

#pragma BEGIN_CONST_DATA

void EXPORT
TweakLogon(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
    AUTOLOGON al;

    hwnd;
    hinst;
    lpszCmdLine;
    nCmdShow;

#ifdef TAMMEB_PERF
    Network_LogQPC(TEXT("StartQPC"));
#endif

    GetStrPkl(al.tszUser, cbX(al.tszUser), &c_klDefUser);
    GetStrPkl(al.tszPass, cbX(al.tszPass), &c_klDefPass);

    /*
     *  Null password is okay.  But make sure there's a user and that
     *  the feature has been enabled.  And skip it all if the shift key
     *  is down.
     */
    if (GetIntPkl(0, &c_klAutoLogon) && al.tszUser[0] &&
        GetAsyncKeyState(VK_SHIFT) >= 0) {
        MSG msg;

        /*
         *  We create our window visible but 0 x 0.
         *
         *  We use a dummy edit control because that lets us extract the
         *  class word for edit controls.
         *
         *  The GWL_USERDATA of the control points to the logon strings.
         *
         */
        hwnd = CreateWindow(TEXT("edit"), "dummy text", WS_POPUP | WS_VISIBLE,
                            0, 0, 0, 0,
                            hwnd, 0, hinstCur, 0);

        SubclassWindow(hwnd, Network_WndProc);
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&al);

        Network_Snoop(hwnd);

        /* Thirty seconds */
        SetTimer(hwnd, 1, 30000, 0);

        while (GetMessage(&msg, 0, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
#ifdef TAMMEB_PERF
    Network_LogQPC(TEXT("StopQPC"));
#endif


}
