// TortoiseSVN - a Windows shell extension for easy version control

// Copyright (C) 2003, 2013-2014, 2018, 2020-2021 - TortoiseSVN
// Copyright (C) 2018-2019 - TortoiseGit

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#include "LoginDialog.h"
#include "TortoisePlinkRes.h"
#include <string>
#include <memory>

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

#define MAX_LENGTH_PASSWORD 256
HINSTANCE g_hModThisDll;
HWND g_hwndMain;

class LoginDialog
{
public:
    LoginDialog(const std::string& prompt);
    ~LoginDialog();

    static bool DoLoginDialog(char* password, int maxlen, const char* prompt);

private:
    bool myOK;
    HWND _hdlg;

    char         myPassword[MAX_LENGTH_PASSWORD];
    std::string  myPrompt;

    void CreateModule(void);
    void RetrieveValues();
    void PurgeValues();

    friend BOOL CALLBACK LoginDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
};


BOOL DoLoginDialog(char* password, int maxlen, const char* prompt)
{
    g_hModThisDll = GetModuleHandle(0);
    g_hwndMain = GetParentHwnd();
    return LoginDialog::DoLoginDialog(password, maxlen, prompt);
}


BOOL CALLBACK LoginDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_INITDIALOG)
    {
        auto pDlg = reinterpret_cast<LoginDialog*>(lParam);
        pDlg->_hdlg = hwndDlg;
        SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
        // Set prompt text
        SendDlgItemMessage(hwndDlg, IDC_LOGIN_PROMPT, WM_SETTEXT,
                           pDlg->myPrompt.length(), (LPARAM) pDlg->myPrompt.c_str());
        SendDlgItemMessage(hwndDlg, IDC_LOGIN_PASSWORD, EM_SETLIMITTEXT, MAX_LENGTH_PASSWORD - 1, 0);
        // Make sure edit control has the focus
        //SendDlgItemMessage(hwndDlg, IDC_LOGIN_PASSWORD, WM_SETFOCUS, 0, 0);
        if (GetDlgCtrlID((HWND) wParam) != IDC_LOGIN_PASSWORD)
        {
           SetFocus(GetDlgItem(hwndDlg, IDC_LOGIN_PASSWORD));
           return FALSE;
        }
        return TRUE;
    }
    else if (uMsg == WM_COMMAND && LOWORD(wParam) == IDCANCEL && HIWORD(wParam) == BN_CLICKED)
    {
        auto pDlg = reinterpret_cast<LoginDialog*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
        pDlg->myOK = false;
        pDlg->PurgeValues();
        EndDialog(hwndDlg, IDCANCEL);
        return 1;
    }
    else if (uMsg == WM_COMMAND && LOWORD(wParam) == IDOK && HIWORD(wParam) == BN_CLICKED)
    {
        auto pDlg = reinterpret_cast<LoginDialog*>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
        pDlg->myOK = true;
        pDlg->RetrieveValues();
        pDlg->PurgeValues();
        EndDialog(hwndDlg, IDOK);
        return 1;
    }
    return 0;
}

LoginDialog::LoginDialog(const std::string& prompt)
{
    myPrompt = prompt;
    SecureZeroMemory(&myPassword, sizeof(myPassword));
}

LoginDialog::~LoginDialog()
{
    SecureZeroMemory(&myPassword, sizeof(myPassword));
}

void LoginDialog::CreateModule(void)
{
    DialogBoxParam(g_hModThisDll, MAKEINTRESOURCE(IDD_LOGIN), g_hwndMain,
                   (DLGPROC)(LoginDialogProc), (LPARAM)this);
}


bool LoginDialog::DoLoginDialog(char* password, int maxlen, const char* prompt)
{
    auto pDlg = std::make_unique<LoginDialog>(prompt);

    pDlg->CreateModule();

    bool ret = pDlg->myOK;

    if (ret)
        strncpy_s(password, maxlen, pDlg->myPassword, sizeof(pDlg->myPassword));

    return ret;
}

void LoginDialog::RetrieveValues()
{
    SendDlgItemMessage(_hdlg, IDC_LOGIN_PASSWORD, WM_GETTEXT, sizeof(myPassword), (LPARAM)myPassword);
}

void LoginDialog::PurgeValues()
{
    // overwrite textfield contents with garbage in order to wipe the cache
    char gargabe[MAX_LENGTH_PASSWORD];
    memset(gargabe, L'*', sizeof(gargabe));
    gargabe[sizeof(gargabe) - 1] = '\0';
    SendDlgItemMessage(_hdlg, IDC_LOGIN_PASSWORD, WM_SETTEXT, 0, (LPARAM)gargabe);
    gargabe[0] = '\0';
    SendDlgItemMessage(_hdlg, IDC_LOGIN_PASSWORD, WM_SETTEXT, 0, (LPARAM)gargabe);
}

HWND GetParentHwnd()
{
    return GetDesktopWindow();
}
